package com.iot08.admin.utils;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iot08.common.entity.admin.RefundApply;
import com.iot08.common.entity.app.OrderRefundInfo;
import com.iot08.common.entity.app.PayOrderInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;

import javax.annotation.PostConstruct;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @version 1.0
 * @Description: 微信支付工具类
 * @time: 2021/2/27 15:18
 * @author: Rock
 */
@Slf4j
@Component
public class WxPayUtil {

    @Value("${wxpay.appid}")
    private String wxpayAppid;

    @Value("${wxpay.mch.id}")
    private String wxpayMchId;

    @Value("${wxpay.mch.key}")
    private String wxpayMchKey;

    @Value("${wxpay.mch.order.url}")
    private String orderUrl;

    @Value("${wxpay.unified.order.url}")
    private String unifiedOrderUrl ="/v3/pay/transactions/app";

    @Value("${wxpay.refund.apply.sign.url}")
    private String refundApplySignUrl = "/v3/refund/domestic/refunds";

    @Value("${wxpay.mac.private.key.path}")
    private String privateKeyPath;

    @Value("${wxpay.serial.no}")
    private String serialNo;

    @Value("${wxpay.aes.key}")
    private String aesKey="pinxuan2024abcde";

    @Value("${wx.platform.cert.path}")
    private String platformCertPath;

    @Value("${wx.platform.cert.serial.no}")
    private String platformCertSerialNo;

    @Value("${wx.api.v3.key}")
    private String apiV3Key;

    @Value("${wxpay.query.order.sign.url}")
    private String queryPaymentOrderSignUrl;

    @Value("${wxpay.query.order.url}")
    private String queryPaymentOrderUrl;

    @Value("${wxpay.pay.refund.url}")
    private String payRefundUrl;

    @Value("${wxpay.query.refund.status.sign.url}")
    private String queryRefundStatusSignUrl;

    @Value("${wxpay.query.refund.status.url}")
    private String queryRefundStatusUrl;

    //请求微信Api的私钥
    private static PrivateKey privateKey;

    //验证微信Api应答、回调的公钥
    private static PublicKey publicKey;

    @PostConstruct
    public void init() {
        try {
            privateKey = KeyUtils.loadPrivateKeyFromPath(privateKeyPath);
            publicKey = KeyUtils.loadPublicKeyFromCert(platformCertPath);
        } catch (Exception e) {
           log.error("加载PrivateKey失败，失败原因如下：{}",e.getMessage());
        }
    }


    /**
     * @description 退款申请
     * @author cwj
     * @param [transactionId, reason, notifyUrl, refundAmount, total, currency]
     * @return com.alibaba.fastjson.JSONObject
     * @date @time 2025/1/22 17:26
     **/
    public JSONObject refundApply(String transactionId,String reason,String notifyUrl,Integer refundAmount,Integer total,String currency,String outRefundId) throws Exception {
        //OffsetDateTime now = OffsetDateTime.now();
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);  // 请求时间戳（秒）
        String nonceStr = WeChatPayUtils.generateNonceStr();  // 随机字符串
        JSONObject params = buildRefundRequestBody(transactionId, reason, notifyUrl, refundAmount, total, currency,outRefundId);
        //构建验签字符串
        String signString = WeChatPayUtils.buildSignString("POST", refundApplySignUrl, timestamp, nonceStr, params.toJSONString());
        log.info("退款申请请求的签名：{}",signString);
        // 对签名串进行 SHA-256 哈希
        //String sha256Hash = WeChatPayUtils.sha256(signString);
        //加载私钥
        loadPrivateKey();
        // 使用私钥对 SHA-256 哈希结果进行 RSA 签名
        String signature = WeChatPayUtils.generateSignature(signString, privateKey);
        //构造 Authorization 头
        String authorizationHeader = WeChatPayUtils.buildAuthorizationHeader(wxpayMchId, nonceStr,signature,timestamp,serialNo);
        return sendPostRequest(payRefundUrl, authorizationHeader, params.toJSONString());
    }



    /**
     * @description 加载privateKey
     * @author cwj
     * @param []
     * @return void
     * @date @time 2025/2/5 16:58
     **/
    private void loadPrivateKey() throws Exception {
        if(ObjectUtils.isEmpty(privateKey)){
            // 读取商户私钥（此处替换为实际私钥路径）
            privateKey = KeyUtils.loadPrivateKeyFromPath(privateKeyPath);
        }
    }


    /**
     * @description 统一下单
     * @author cwj
     * @param [outTradeNo, body, totalFee, notifyUrl]
     * @return java.util.Map<java.lang.String,java.lang.String>
     * @date @time 2025/1/2 15:22
     **/
    public JSONObject unifiedOrder(String outTradeNo, String desc, int totalFee, String notifyUrl,String ip) throws Exception {
        OffsetDateTime now = OffsetDateTime.now();
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);  // 请求时间戳（秒）
        String nonceStr = WeChatPayUtils.generateNonceStr();  // 随机字符串
        //构建下单参数
        JSONObject params = buildRequestBody(outTradeNo, desc, totalFee, notifyUrl, ip, now);
        //构建验签字符串
        String signString = WeChatPayUtils.buildSignString("POST", unifiedOrderUrl, timestamp, nonceStr, params.toJSONString());
        log.info("统一下单验签字符串为：{}",signString);
        // 对签名串进行 SHA-256 哈希
        //String sha256Hash = WeChatPayUtils.sha256(signString);
        loadPrivateKey();
        // 使用私钥对 SHA-256 哈希结果进行 RSA 签名
        String signature = WeChatPayUtils.generateSignature(signString, privateKey);

        //构造 Authorization 头
        String authorizationHeader = WeChatPayUtils.buildAuthorizationHeader(wxpayMchId, nonceStr,signature,timestamp,serialNo);
        //发送请求
        JSONObject json = sendPostRequest(orderUrl, authorizationHeader, params.toJSONString());
        String prepayId;
        if(ObjectUtils.isNotEmpty(json)){
            // 获取 prepay_id
            prepayId = json.getString("prepay_id");
            if(StringUtils.isEmpty(prepayId)){
                return null;
            }
        } else {
            return null;
        }
        //组装支付所需参数返回给前端
        JSONObject jsonObject = new JSONObject();
        buildPaymentParam(jsonObject, prepayId, timestamp);
        // 创建请求
        return jsonObject;
    }


    /**
     * @description 查询微信支付订单信息
     * @author cwj
     * @param [outTradeNo]
     * @return void
     * @date @time 2025/1/7 14:16
     **/
    public String queryWxPaymentOrder(String outTradeNo) throws Exception {
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);  // 请求时间戳（秒）
        String nonceStr = WeChatPayUtils.generateNonceStr();  // 随机字符串
        String signUrl = queryPaymentOrderSignUrl + outTradeNo+"?mchid="+wxpayMchId;
        String reqUrl = queryPaymentOrderUrl + outTradeNo;
        //构建请求签名信息
        String authorizationHeader = buildAuthHeader(signUrl, timestamp, nonceStr);
        //组装请求链接
        reqUrl = UriComponentsBuilder.fromHttpUrl(reqUrl)
                .queryParam("mchid", wxpayMchId)  // 添加 mchid
                .toUriString();
        return sendQueryGetReqs(reqUrl,authorizationHeader);
    }
    
    /**
     * @description 查询退款订单的状态信息
     * @author cwj
     * @param [refundId]
     * @return java.lang.String
     * @date @time 2025/2/10 15:09
     **/
    public String queryRefundStatueInfo(String refundId) throws Exception {
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);  // 请求时间戳（秒）
        String nonceStr = WeChatPayUtils.generateNonceStr();  // 随机字符串
        String signUrl = queryRefundStatusSignUrl + refundId;
        String reqUrl = queryRefundStatusUrl + refundId;
        //构建签名字符串
        String authorizationHeader = buildAuthHeader(signUrl, timestamp, nonceStr);
        return sendQueryGetReqs(reqUrl,authorizationHeader);
    }

    /**
     * @description 构建请求的签名信息
     * @author cwj
     * @param [signUrl, timestamp, nonceStr]
     * @return java.lang.String
     * @date @time 2025/2/10 15:29
     **/
    private String buildAuthHeader(String signUrl, String timestamp, String nonceStr) throws Exception {
        //构建签名字符串
        String signString = WeChatPayUtils.buildSignString("GET", signUrl, timestamp, nonceStr,"");
        loadPrivateKey();
        // 使用私钥对 SHA-256 哈希结果进行 RSA 签名
        String signature = WeChatPayUtils.generateSignature(signString, privateKey);
        return WeChatPayUtils.buildAuthorizationHeader(wxpayMchId, nonceStr,signature, timestamp,serialNo);
    }

    /**
     * @description 执行查询
     * @author cwj
     * @param [reqUrl, authorizationHeader]
     * @return java.lang.String
     * @date @time 2025/1/7 14:55
     **/
    private String sendQueryGetReqs(String reqUrl, String authorizationHeader) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                HttpGet getReq = new HttpGet(reqUrl);
                log.info("Authorization:{}", authorizationHeader);
                // 设置请求头
                settingGetRequestHead(authorizationHeader, getReq);
                // 发送请求并获取响应
                HttpResponse response = httpClient.execute(getReq);
                //查询状态码
                int statusCode = response.getStatusLine().getStatusCode();
                String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8");
                // 打印返回结果
                log.info("微信退款订单查询的响应结果:{}", responseBody);
                if(statusCode == 200){
                    return responseBody;
                }
                else {
                    return null;
                }
           } catch (Exception ex){
              log.error("请求微信退款订单查询接口失败，失败提示信息为：{}",ex.getMessage());
              return null;
        }
    }

    /**
     * @description 构建支付所需参数
     * @author cwj
     * @param [jsonObject, prepayId, timestamp, signString]
     * @return void
     * @date @time 2025/1/3 9:38
     **/
    private void buildPaymentParam(JSONObject jsonObject, String prepayId, String timestamp) throws Exception {
        String nonceStr = WeChatPayUtils.generateNonceStr();
        jsonObject.put("appid", AESUtil.encrypt(wxpayAppid,aesKey));
        jsonObject.put("mechid",AESUtil.encrypt(wxpayMchId,aesKey));
        jsonObject.put("prepayId", prepayId);
        jsonObject.put("packageValue","Sign=WXPay");
        jsonObject.put("nonceStr",nonceStr);
        jsonObject.put("timeStamp", timestamp);
        String signStr = assemblePaymentSign(timestamp, nonceStr, prepayId);
        log.info("支付签名字符串：{}",signStr);
        //组装签名
        jsonObject.put("sign",signStr);
    }


    /**
     * @description 组装支付签名
     * @author cwj
     * @param [timestamp, nonceStr, prepayId]
     * @return java.lang.String
     * @date @time 2025/1/3 16:11
     **/
    private String  assemblePaymentSign(String timestamp, String nonceStr, String prepayId) throws Exception {
        //组装支付签名字段
        String paymentSignStr = WeChatPayUtils.buildPaySignString(wxpayAppid, timestamp, nonceStr, prepayId);
        log.info("支付验签字符串为：{}",paymentSignStr);
        // 使用私钥对 SHA-256 哈希结果进行 RSA 签名
        return WeChatPayUtils.generateSignature(paymentSignStr, privateKey);
    }


    /**
     * @description 构建下单的请求参数体
     * @author cwj
     * @param [outTradeNo, desc, totalFee, notifyUrl, ip, now]
     * @return com.alibaba.fastjson.JSONObject
     * @date @time 2025/1/2 16:19
     **/
    private JSONObject buildRequestBody(String outTradeNo, String desc, int totalFee, String notifyUrl, String ip, OffsetDateTime now) {
        String timeExpire = now.plusHours(2).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
        JSONObject params = new JSONObject();
        params.put("appid", wxpayAppid);  // 小程序或者公众号的 AppID
        params.put("mchid", wxpayMchId);  // 商户号
        params.put("description", desc);   // 商品描述
        params.put("out_trade_no", outTradeNo);  // 商户订单号
        params.put("time_expire",timeExpire);  //支付结束时间
        params.put("notify_url", notifyUrl);  // 支付回调通知地址
        params.put("support_fapiao",false);   //电子发票入口开放标识
        JSONObject amount = new JSONObject();
        amount.put("total", totalFee);  //订单金额，单位是分
        amount.put("currency","CNY");  //币种
        params.put("amount",amount);
        if(StringUtils.isNotEmpty(ip))
        {
            JSONObject sceneInfo = new JSONObject();
            sceneInfo.put("payer_client_ip", ip);
            params.put("scene_info",sceneInfo);
        }
        JSONObject settleInfo = new JSONObject();
        settleInfo.put("profit_sharing",false);
        params.put("settle_info",settleInfo);
        return params;
    }

    /**
     * @description
     * @author cwj
     * @param [transactionId, reason, notifyUrl, refundAmount, total, currency]
     * @return com.alibaba.fastjson.JSONObject
     * @date @time 2025/1/22 18:14
     **/
    private JSONObject buildRefundRequestBody(String transactionId, String reason, String notifyUrl, Integer refundAmount, Integer total, String currency,String outRefundId) {
        JSONObject params = new JSONObject();
        params.put("transaction_id", transactionId);   // 商品描述
        params.put("out_refund_no",outRefundId);  //退款单号
        params.put("reason", reason);  //退款原因
        params.put("notify_url", notifyUrl);  // 退款回调通知地址
        JSONObject amount = new JSONObject();
        amount.put("refund", refundAmount);   //退款金额
        amount.put("total", total);   //原订单金额
        amount.put("currency", currency);  //币种
        params.put("amount",amount);   //订单退款金额信息
        return params;
    }



    /**
     * @description 发送POST请求
     * @author cwj
     * @param [url, authorizationHeader, requestBody]
     * @return com.iot08.common.entity.app.OrderRefundInfo
     * @date @time 2025/1/23 16:35
     **/
    public JSONObject sendPostRequest(String url, String authorizationHeader, String requestBody){
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost post = new HttpPost(url);
            log.info("Authorization:{}",authorizationHeader);
            // 设置请求头
            settingPostRequestHead(authorizationHeader, post);
            // 设置请求体
            StringEntity entity = new StringEntity(requestBody, "UTF-8");
            post.setEntity(entity);
            // 发送请求并获取响应
            HttpResponse response = httpClient.execute(post);
            //查询状态码
            int statusCode = response.getStatusLine().getStatusCode();
            String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8");
            // 打印返回结果
            log.info("响应结果:{}", responseBody);
            if(statusCode == 200){
                // 解析返回的 JSON 数据
                ObjectMapper objectMapper = new ObjectMapper();
                return objectMapper.readValue(responseBody, JSONObject.class);
            }else {
                return null;
            }
        }catch (Exception ex){
            log.error("请求微信接口时发声异常，异常信息为：{}",ex.getMessage());
        }
        return null;
    }



    /**
     * @description 设置POST请求头部
     * @author cwj
     * @param [authorizationHeader, post]
     * @return void
     * @date @time 2025/1/7 14:34
     **/
    public static void settingPostRequestHead(String authorizationHeader, HttpPost post) {
        post.setHeader("Authorization", authorizationHeader);
        post.setHeader("Content-Type", "application/json");
        post.setHeader("Accept", "application/json");  // 添加 Accept 请求头
    }

    /**
     * @description 设置GET请求的请求头部
     * @author cwj
     * @param [authorizationHeader, getReq]
     * @return void
     * @date @time 2025/1/7 17:36
     **/
    public static void settingGetRequestHead(String authorizationHeader, HttpGet getReq) {
        getReq.setHeader("Authorization", authorizationHeader);
        getReq.setHeader("Content-Type", "application/json");
        getReq.setHeader("Accept", "application/json");  // 添加 Accept 请求头
    }
    


    /**
     * @description 比对平台证书的序列号
     * @author cwj
     * @param [serialNo]
     * @return boolean
     * @date @time 2025/1/4 10:04
     **/
    public boolean comparePlatformCertSerialNo(String serialNo) {
        return platformCertSerialNo.equals(serialNo);
    }
    
    /**
     * @description 验证签名
     * @author cwj
     * @param [signature, signStr]
     * @return boolean
     * @date @time 2025/1/4 10:08
     **/
    public boolean verifySignature(String signature, String signStr) throws Exception {
        return WeChatPayUtils.verifySignature(publicKey,signature,signStr);
    }


    /**
     * @description
     * @author cwj
     * @param [associatedData, nonce, ciphertext]
     * @return java.lang.String
     * @date @time 2025/1/4 11:15
     **/
    public String decryptResource(String associatedData,String nonce,String ciphertext) throws Exception {
        return AESUtil.decryptResource(associatedData,nonce,ciphertext,apiV3Key);
    }

    /**
     * @param parameters
     * @time: 2021/2/27 15:30
     * @author: Rock
     * @version 1.0
     */
    public String getParamString(SortedMap<String, String> parameters) {
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        StringBuffer sb = new StringBuffer("<xml>");
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            sb.append("<" + k + ">" + v + "</" + k + ">");
        }
        sb.append("</xml>");
        return sb.toString();
    }

    public String createMD5Sign(SortedMap<String, String> parameters) {
        StringBuilder sb = new StringBuilder();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            sb.append(k + "=" + v + "&");
        }
        String params = sb.append("key=" + wxpayMchKey).substring(0);
        String sign = MD5Util.md5Encode(params, "utf8");
        sign = sign.toUpperCase();
        return sign;
    }

    public static String getRandomString(int length) {
        //随机字符串的随机字符库
        String keyString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder sb = new StringBuilder();
        int len = keyString.length();
        for (int i = 0; i < length; i++) {
            sb.append(keyString.charAt((int) Math.round(Math.random() * (len - 1))));
        }
        return sb.toString();
    }




   /* public static void main(String args[]){
        //System.out.println(getRandomString(32));
        System.out.println(new ObjectId());
    }*/

}
